package customer

import (
	"context"
	stdErrors "errors"
	"fmt"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/google/wire"
	"go.manyji.com/lib/encoding/json"
	"go.manyji.com/lib/mysql"
	"go.manyji.com/lib/util/convert"
	"go.manyji.com/lib/util/errors"
	"reflect"
	"sieve_admin_server/app/dao"
	"sieve_admin_server/app/dao/business_price_scheme"
	"sieve_admin_server/app/schema"
	schemaCustomer "sieve_admin_server/app/schema/customer"
	serviceSys "sieve_admin_server/app/service/sys"
	serviceUtil "sieve_admin_server/app/service/util"
	pkgTime "sieve_admin_server/pkg/time"
	"sieve_admin_server/pkg/util"
	"strconv"
	"strings"
	"time"
)

var (
	BusinessPriceSet = wire.NewSet(wire.Struct(new(BusinessPriceSrv), "*"))
)

type BusinessPriceSrv struct {
	Trans                   *dao.Trans
	UserSrv                 *UserSrv
	UserRepo                *dao.UserRepo
	RoleSrv                 *serviceSys.RoleSrv
	UserAuthSrv             *UserAuthSrv
	BusinessPriceSchemeRepo *business_price_scheme.BusinessPriceSchemeRepo
}

// 规则为：不得低于平台价格。不得高于平台价格两倍
func (a *BusinessPriceSrv) checkBusinessPrice(ctx context.Context, uid int, prices json.RawMessage) error {

	var (
		name2Price = map[string]uint64{}
		bizTyData  []schemaCustomer.BusinessTypeQueryRsp
	)
	if err := json.Unmarshal(prices, &bizTyData); err != nil {
		return err
	} else {
		for _, v := range bizTyData {
			name2Price[v.Name] = v.UnitPrice
		}
	}
	resp := a.QueryDetail(ctx, uid, map[string]interface{}{
		"def": 1,
	})
	if resp == nil {
		return nil
	}
	if v, ok := resp.(error); ok {
		return v
	}

	if len(name2Price) < len(resp.([]interface{})) {
		return stdErrors.New(fmt.Sprintf("请设置所有的业务类型价格后再提交"))
	}

	for _, v := range resp.([]interface{}) {
		item := v.(*schemaCustomer.BusinessTypeQueryRsp)
		if setPrice, ok := name2Price[item.Name]; ok {
			if !(setPrice >= item.UnitPrice && setPrice <= item.UnitPrice*2) {
				return stdErrors.New(fmt.Sprintf("设置的'%v(%v)'的价格(%v)不在有效范围内(%v-%v)",
					item.Desc,
					item.Name, float64(setPrice)/10000.0, float64(item.UnitPrice)/10000, float64(item.UnitPrice*2)/10000))
			}
		}
	}

	return nil
}

func (a *BusinessPriceSrv) GetByName(ctx context.Context, name string) (*business_price_scheme.BusinessPriceScheme, error) {

	var (
		tableName = business_price_scheme.BusinessPriceScheme{}
		querySQL  = fmt.Sprintf(`
	SELECT * FROM %v 
	WHERE name = '%v'
	AND deleted = 0
`, tableName.TableName(), name)
	)

	req, err := a.BusinessPriceSchemeRepo.QueryRaw(ctx, querySQL, business_price_scheme.BusinessPriceScheme{})
	if err != nil {
		return nil, err
	}

	if req == nil {
		return nil, errors.New(fmt.Sprintf("record not  found"))
	}

	return req[0].(*business_price_scheme.BusinessPriceScheme), nil
}

func (a *BusinessPriceSrv) Create(ctx context.Context, uid int, item map[string]interface{}) interface{} {

	if _, ok := item["name"]; !ok {
		return stdErrors.New(fmt.Sprintf("'方案名称'为必填项"))
	}

	if _, ok := item["prices"]; !ok {
		return stdErrors.New(fmt.Sprintf("'方案价格'为必填项"))
	}

	respUser, err := a.UserSrv.GetByUID(ctx, uint64(uid))
	if err != nil {
		return err
	}

	if !a.RoleSrv.IsAdminRoleId(respUser.RoleId) {
		return stdErrors.New(fmt.Sprintf("只有管理角色才可以新增'业务方案配置'"))
	}

	if data, err := a.GetByName(ctx, item["name"].(string)); err == nil && data != nil && data.Name == item["name"].(string) {
		return stdErrors.New("已存在相同的方案名称")
	}

	if v, ok := item["def"]; !ok || (ok && v.(uint64) != 1) {
		if err := a.checkBusinessPrice(ctx, uid, json.RawMessage(item["prices"].(string))); err != nil {
			return err
		}
	}

	var roleID uint64
	if v, ok := item["role_id"]; ok {
		roleID = v.(uint64)
	} else {
		if resp, err := a.UserSrv.GetRoleID(ctx, uint64(uid)); err != nil {
			return err
		} else {
			roleID = resp
		}
	}

	var (
		now = time.Now().Unix()
		def = map[string]interface{}{
			"create_at":  now,
			"updated_at": now,
			"role_id":    roleID,
			"creator":    uid,
		}
	)

	for k, v := range def {
		item[k] = v
	}

	return a.BusinessPriceSchemeRepo.Create(ctx, item)
}

func (a *BusinessPriceSrv) QueryDetail(ctx context.Context, uid int, reqArgs map[string]interface{}) interface{} {

	uid = int(adminAccount.ID)
	if resp, err := a.UserSrv.GetRoleID(ctx, uint64(uid)); err != nil {
		return err
	} else {
		reqArgs["role_id"] = resp
	}

	var names []string
	if resp := a.Query(ctx, uid, reqArgs, schema.PaginationParam{
		OnlyData: true,
	}); resp != nil {
		if err, ok := resp.(error); ok {
			return err
		}

		vData := resp.(map[string]interface{})["list"].([]interface{})
		if len(vData) == 0 {
			return nil
		}

		b := json.RawMessage(vData[0].(*schemaCustomer.BusinessPriceQueryRsp).Prices)
		var vv []schemaCustomer.BusinessTypeQueryRsp
		if err := json.Unmarshal(b, &vv); err != nil {
			return err
		} else {
			for _, v := range vv {
				names = append(names, fmt.Sprintf("'%v'", v.Name))
			}
		}
	}

	if len(names) == 0 {
		return nil
	}

	var (
		querySQL = fmt.Sprintf(`
	SELECT
		id,
		name,
		%v,
		unit_price
	FROM
	    business_type_configuration
	WHERE 
	    name IN (%v) AND sieve_source_config_id > 0 AND status = 1 AND deleted = 0 
`, "`desc`", strings.Join(names, ","))
	)

	respBiz, err := a.BusinessPriceSchemeRepo.QueryRaw(ctx, querySQL, schemaCustomer.BusinessTypeQueryRsp{})
	if err != nil {
		return err
	}

	return respBiz
}

func (a *BusinessPriceSrv) Query(ctx context.Context, uid int, reqArgs map[string]interface{}, p schema.PaginationParam) interface{} {

	uid = int(adminAccount.ID)
	if resp, err := a.UserSrv.GetRoleID(ctx, uint64(uid)); err != nil {
		return err
	} else {
		reqArgs["role_id"] = resp
	}

	var (
		withDef bool
		repKeys = util.Struct2Slice(schemaCustomer.BusinessPriceQueryRsp{}, "json", "business_price_scheme")
	)

	if _, ok := reqArgs["name"]; ok {
		ctx = context.WithValue(ctx,
			"condition", map[string]string{"name": "LIKE"},
		)

		reqArgs["name"] = "'%" + reqArgs["name"].(string) + "%'"
	}

	if _, ok := reqArgs["description"]; ok {
		ctx = context.WithValue(ctx,
			"condition", map[string]string{"description": "LIKE"},
		)

		reqArgs["description"] = "'%" + reqArgs["description"].(string) + "%'"
	}

	ctx = context.WithValue(ctx, "column_types", schemaCustomer.BusinessPriceQueryRsp{})

	if v, ok := reqArgs["with_def"]; ok {
		withDef = v.(uint64) == 1
	}

	var defData *schemaCustomer.BusinessPriceQueryRsp
	if withDef {
		var (
			filedKeys = repKeys
			queryKeys = map[string]interface{}{
				"def": 1,
			}
			pp = schema.PaginationParam{
				OnlyData: true,
			}
		)

		if resp, err := a.BusinessPriceSchemeRepo.Query(ctx, queryKeys, pp, filedKeys); err != nil {
			return err
		} else {
			v := resp.(map[string]interface{})["list"].([]interface{})
			if len(v) > 0 {
				defData = v[0].(*schemaCustomer.BusinessPriceQueryRsp)
			}
		}
		reqArgs["def"] = 2
		delete(reqArgs, "with_def")
	}

	req, err := a.BusinessPriceSchemeRepo.Query(ctx, reqArgs, p, repKeys)
	if err != nil {
		return err
	}

	if withDef && defData != nil {
		if len(req.(map[string]interface{})["list"].([]interface{})) > 0 {
			return map[string]interface{}{
				"list": append([]interface{}{defData}, req.(map[string]interface{})["list"].([]interface{})...),
				//"total": req.(map[string]interface{})["total"].(uint64) + 1,
				"total": req.(map[string]interface{})["total"].(uint64),
			}
		}
	}
	return req
}

func (a *BusinessPriceSrv) Delete(ctx context.Context, uid uint64, reqArgs map[string]interface{}) interface{} {

	if _, ok := reqArgs["id"]; !ok {
		return stdErrors.New("请求参数缺失")
	}

	if _, ok := reqArgs["new_id"]; !ok {
		return stdErrors.New("请求参数缺失")
	}

	var (
		id, _ = strconv.ParseUint(fmt.Sprintf("%v", reqArgs["id"]), 10, 64)
	)

	respUser, err := a.UserSrv.GetByUID(ctx, uid)
	if err != nil {
		return err
	}

	if !a.RoleSrv.IsAdminRoleId(respUser.RoleId) {
		return stdErrors.New(fmt.Sprintf("只有管理员角色才可以删除"))
	}

	if resp := a.Query(ctx, int(uid), map[string]interface{}{
		"id": reqArgs["id"],
	}, schema.PaginationParam{
		OnlyData: true,
	}); resp != nil {
		if v, ok := resp.(error); ok {
			return v
		}
		items := resp.(map[string]interface{})["list"].([]interface{})
		if len(items) <= 0 {
			return stdErrors.New("要删除的业务价格配置项不存在")
		}
		if items[0].(*schemaCustomer.BusinessPriceQueryRsp).Default == 1 {
			return stdErrors.New("默认配置项不可被删除")
		}
	} else {
		return stdErrors.New("要删除的业务价格配置项不存在")
	}

	err = a.Trans.TransactionTx(ctx, func(ctx context.Context, t *mysql.Tx) error {

		var (
			updateKeys = map[string]interface{}{
				"business_price_id": reqArgs["new_id"],
			}
			queryKeys = map[string]interface{}{
				"business_price_id": reqArgs["id"],
			}
		)

		if err := a.UserSrv.UpdateBusinessPriceUserCount(ctx, reqArgs["new_id"].(uint64), false); err != nil {
			return err
		}

		if err := a.UserRepo.Update(ctx, updateKeys, queryKeys); err != nil {
			return err
		}

		return a.BusinessPriceSchemeRepo.SoftDelete(ctx, id)
	})

	return err
}

func (a *BusinessPriceSrv) Update(ctx context.Context, uid int, reqArgs map[string]interface{}) error {

	if _, ok := reqArgs["id"]; !ok {
		return stdErrors.New("请求参数缺失")
	}
	respUser, err := a.UserSrv.GetByUID(ctx, uint64(uid))
	if err != nil {
		return err
	}

	if !a.RoleSrv.IsAdminRoleId(respUser.RoleId) {
		return errors.New(fmt.Sprintf("只有管理员角色才可以修改"))
	}

	var (
		now        = time.Now().Unix()
		updateKeys = map[string]interface{}{
			"updated_at":  now,
			"name":        reqArgs["name"],
			"description": reqArgs["description"],
			"prices":      reqArgs["prices"],
			"creator":     uint64(uid),
		}
		queryKeys = map[string]interface{}{
			"id": reqArgs["id"],
		}
	)

	if v, ok := reqArgs["role_id"]; ok {
		updateKeys["role_id"] = v
	} else {
		if resp, err := a.UserSrv.GetRoleID(ctx, uint64(uid)); err != nil {
			return err
		} else {
			queryKeys["role_id"] = resp
		}
	}

	if data, err := a.GetByName(ctx, reqArgs["name"].(string)); err == nil && data != nil && data.Name != reqArgs["name"].(string) {
		return stdErrors.New("不存在的方案名称")
	}

	if v, ok := reqArgs["def"]; !ok || (ok && v.(uint64) != 1) {
		if err := a.checkBusinessPrice(ctx, uid, json.RawMessage(reqArgs["prices"].(string))); err != nil {
			return err
		}
	}

	return a.BusinessPriceSchemeRepo.Update(ctx, updateKeys, queryKeys)
}

func (a *BusinessPriceSrv) Export(ctx context.Context, uid int, reqArgs map[string]interface{}) interface{} {

	var (
		p = schema.PaginationParam{
			OnlyData: true,
		}
		queryArgs = map[string]interface{}{
			"id": func() string {
				ids := reqArgs["ids"].(string)
				return fmt.Sprintf("(%v)", strings.Join(strings.Split(ids, "|"), ","))
			}(),
		}
	)

	ctx = context.WithValue(ctx,
		"condition", map[string]string{"id": "IN"},
	)

	respQuery := a.Query(ctx, uid, queryArgs, p)
	if respQuery != nil {
		if _, ok := respQuery.(error); ok && respQuery != nil {
			return respQuery
		}
	}

	selectedItems := []string{
		"id",
		"name",
		"description",
		"user_count",
		"updated_at",
	}
	respQuery = a.export(ctx, respQuery, strings.Join(selectedItems, "|"))
	if respQuery != nil {
		if _, ok := respQuery.(error); ok {
			return respQuery
		}
	}

	return respQuery
}

func (a *BusinessPriceSrv) export(ctx context.Context, req interface{}, selectedItems string) interface{} {

	var (
		selectedItemMap = make(map[string]int)
		xlsx            = excelize.NewFile()
		categories      = map[string]string{"A1": "序号", "B1": "业务方案", "C1": "说明", "D1": "用户数", "E1": "最后修改时间"}
		colWidths       = map[string]float64{"A": 5, "B": 10, "C": 15, "D": 10, "E": 20}
	)

	for key, value := range categories {
		xlsx.SetCellValue("Sheet1", key, value)
		_style, err := xlsx.NewStyle(`{"fill":{"type":"pattern","color":["#EEECE1"],"pattern":1},"alignment":{"horizontal":"center","vertical":"center"},"font":{"bold":true,"family":"宋体","size":10}}`)
		if err == nil {
			xlsx.SetCellStyle("Sheet1", key, key, _style)
		}
	}

	for k, v := range colWidths {
		xlsx.SetColWidth("Sheet1", k, k, v)
	}

	for i, v := range strings.Split(fmt.Sprintf("%v", selectedItems), "|") {
		selectedItemMap[v] = i
	}
	v := req.(map[string]interface{})
	for index, vv := range v["list"].([]interface{}) {

		var (
			columIndex  = 'A'
			columOffset = 1
		)
		for kkk, vvv := range util.Struct2Map(vv, "json") {

			if len(selectedItemMap) > 0 {
				if i, ok := selectedItemMap[kkk]; !ok {
					continue
				} else {
					columOffset = i
				}
			}

			cell := fmt.Sprintf("%c", int(columIndex)+columOffset) + convert.ToString(index+2)

			if reflect.TypeOf(vvv).Kind() == reflect.Ptr {
				vOf := reflect.ValueOf(vvv)
				if vOf.IsNil() {
					vvv = ""
				} else {
					vvv = vOf.Elem()
				}
			}

			if kkk == "updated_at" {
				vvv = pkgTime.Format(vvv.(int64))
			}

			xlsx.SetCellValue("Sheet1", cell, vvv)
		}
		xlsx.SetRowHeight("Sheet1", index+2, 18)
	}

	xlsx.SetSheetName("Sheet1", "第一页")

	filPath, fileName := a.getExportPath(convert.ToString(time.Now().UnixMilli())+".csv", 1)
	err := xlsx.SaveAs(filPath)
	if err != nil {
		return err
	}

	req = fileName

	return req
}

func (a *BusinessPriceSrv) getExportPath(fileName string, bizTy int) (string, string) {

	var (
		bizTyMap = map[int]string{
			1: "business_price",
		}
	)

	relativePath := fmt.Sprintf("%v/export/%v", bizTyMap[bizTy], fileName)

	return serviceUtil.GetFilePath(
		fmt.Sprintf("%v/%v",
			serviceUtil.FileStorage(),
			relativePath),
	), relativePath
}
